#include <tchar.h>
#include <string.h>
#include <objbase.h>

#include "lib.h"
#include "bfish.h"
#include "blowfish.h"
#include "blowfishkey.h"

//#include <comutil.h>

#define SUPER Encryption

int32
BlowfishEncryption::encode(uint8 * src, int32 len)
{
	return encode(src, src, len);
}

int32
BlowfishEncryption::encode(uint8 * src, uint8 * dst, int32 len)
{
	BF_KEY	BFkey;
	uint8	ASCkey[BLOWFISH_KEY_LEN + 1];
	uint8	IVECbuffer[BLOWFISH_IVEC_LEN + 1];
	int		bufcount=0;

	if (len > 0)
	{
		// Now use 40-bit Blowfish
		memcpy(ASCkey, BLOWFISH_KEY, BLOWFISH_KEY_LEN);
		ASCkey[BLOWFISH_KEY_LEN] = 0;

		memcpy(IVECbuffer, BLOWFISH_IVEC, BLOWFISH_IVEC_LEN);
		IVECbuffer[BLOWFISH_IVEC_LEN] = 0;

		BF_set_key(&BFkey, BLOWFISH_KEY_LEN, (uint8*)BLOWFISH_KEY);

		// Use CFB mode so we can encrypt an arbitrary number of bytes
		BF_cfb64_encrypt(
			src, dst, len, &BFkey, IVECbuffer, &bufcount, BF_ENCRYPT);

		return len;
	}
	
	return 0;
}

// This method assumes that a maximum key size of 128 bits will be sufficient
// (hence "16" below) -- it still uses CFB mode, however, so an 8-byte 
// initialization vector is still necessary, regardless of key length
int32
BlowfishEncryption::encode(uint8 * src, uint8 * dst, int32 len, uint8 * key,
	uint8 keylen, uint8 * ivec, uint8 iveclen)
{
	BF_KEY	BFKey;
	uint8	ASCkey[16 + 1];
	uint8	IVECbuffer[8 + 1];
	int		bufcount = 0;

	if (len > 0 && keylen <= 16 && iveclen == 8)
	{
		memcpy(ASCkey, key, keylen);
		memcpy(IVECbuffer, ivec, iveclen);

		BF_set_key(&BFKey, keylen, ASCkey);

		// Use CFB mode so we can encrypt an arbitrary number of bytes
		BF_cfb64_encrypt(src, dst, len, &BFKey, IVECbuffer, &bufcount,
			BF_ENCRYPT);

		return len;
	}

	return 0;
}

int32
BlowfishEncryption::decode(uint8* src, int32 len)
{
	return decode(src, src, len);
}

int32
BlowfishEncryption::decode(uint8* src, uint8* dst, int32 len)
{
	BF_KEY	BFkey;
	uint8	ASCkey[BLOWFISH_KEY_LEN + 1];
	uint8	IVECbuffer[BLOWFISH_IVEC_LEN + 1];
	int		bufcount=0;

	if (len > 0)
	{
		// Now use 40-bit Blowfish
		memcpy(ASCkey, BLOWFISH_KEY, BLOWFISH_KEY_LEN);
		ASCkey[BLOWFISH_KEY_LEN] = 0;

		memcpy(IVECbuffer, BLOWFISH_IVEC, BLOWFISH_IVEC_LEN);
		IVECbuffer[BLOWFISH_IVEC_LEN] = 0;

		BF_set_key(&BFkey, BLOWFISH_KEY_LEN, (uint8*)BLOWFISH_KEY);

		// Use CFB mode so we can encrypt an arbitrary number of bytes
		BF_cfb64_encrypt(
			src, dst, len, &BFkey, IVECbuffer, &bufcount, BF_DECRYPT);

		return len;
	}

	return 0;
}

// This method assumes that a maximum key size of 128 bits will be sufficient
// (hence "16" below) -- it still uses CFB mode, however, so an 8-byte 
// initialization vector is still necessary, regardless of key length
int32
BlowfishEncryption::decode(uint8 * src, uint8 * dst, int32 len, uint8 * key,
	uint8 keylen, uint8 * ivec, uint8 iveclen)
{
	BF_KEY	BFKey;
	uint8	ASCkey[16 + 1];
	uint8	IVECbuffer[8 + 1];
	int		bufcount = 0;

	if (len > 0 && keylen <= 16 && iveclen == 8)
	{
		memcpy(ASCkey, key, keylen);
		memcpy(IVECbuffer, ivec, iveclen);

		BF_set_key(&BFKey, keylen, ASCkey);

		// Use CFB mode so we can encrypt an arbitrary number of bytes
		BF_cfb64_encrypt(src, dst, len, &BFKey, IVECbuffer, &bufcount,
			BF_DECRYPT);

		return len;
	}

	return 0;
}


#ifndef BUILDING_COM_PROJECTS

String BlowfishEncryption::EncryptString(const wchar_t* szValue)
{
    // Create a buffer for the encrypted string
    int nValueLen = wcslen(szValue);
    ASSERT(nValueLen > 0);      // cannot be an empty value
    wchar_t* bufEncrypted = new wchar_t[nValueLen+1];

    // Encrypt this string using Blowfish Encryption
    BlowfishEncryption bf;
    int lenEncrypt = bf.encode((uint8*)szValue, (uint8*)bufEncrypted, nValueLen * sizeof(wchar_t));
    ASSERT(lenEncrypt == (int)(nValueLen * sizeof(wchar_t)));
    bufEncrypted[nValueLen] = L'\0';

    // Each wide character in string is stored as a 4-byte hex string
    String strEncrypted;
    strEncrypted.getBuffer(nValueLen * 4);
	strEncrypted.releaseBuffer();
    for (int i = 0; i < nValueLen; i ++)
    {
        String strHexChar;
        strHexChar.sprintf(_T("%04x"), (uint32)(uint16)bufEncrypted[i]);
        strEncrypted += strHexChar;
    }

    // Clean up
    delete [] bufEncrypted;
    bufEncrypted = NULL;

#ifdef _DEBUG
    // DEBUG CODE -- decrypting the encrypted string should produce the original source value
    String source_value = szValue;
    String debug_decrypted = BlowfishEncryption::DecryptString(strEncrypted);
    ASSERT(debug_decrypted == source_value);
#endif

    // Convert encrypted buffer into String
    return strEncrypted;
}

#define NUMBER_CONVERT_RADIX 36

String BlowfishEncryption::EncryptUInt32(uint32 nValue)
{
    // First, format the uint32 value as a hex string
    wchar_t szValue[64];
    _ui64tow_s(nValue, szValue, _countof(szValue), NUMBER_CONVERT_RADIX);

    // Now encrypt this string
    return EncryptString(szValue);
}

static int32 HexCharToInt(wchar_t value)
{
    if (value >= L'0'  &&  value <= L'9')
    {
        return value - L'0';
    }
    else if (value >= L'a'  &&  value <= L'f')
    {
        return value - L'a' + 10;
    }
    else if (value >= L'A'  &&  value <= L'F')
    {
        return value - L'A' + 10;
    }

    ASSERT(false);  // bad input value
    return 0;
}

String BlowfishEncryption::DecryptString(const wchar_t* szValue)
{
    int nValueLen = wcslen(szValue);
    ASSERT(nValueLen > 0);      // cannot be an empty value
    ASSERT(nValueLen % 4 == 0); // length must be a multiple of 4
    wchar_t* bufEncrypted = new wchar_t[nValueLen+1];

    // Each wide character in string is stored as a 4-byte hex string.
    // These 4-byte hex values must now be converted back to funky wide chars.
    for (int i = 0; i < nValueLen; i += 4)
    {
        uint32 nibble4 = (HexCharToInt(szValue[i+0]) << 12);
        uint32 nibble3 = (HexCharToInt(szValue[i+1]) << 8);
        uint32 nibble2 = (HexCharToInt(szValue[i+2]) << 4);
        uint32 nibble1 = (HexCharToInt(szValue[i+3]) << 0);
        uint32 charValue = nibble4 + nibble3 + nibble2 + nibble1;
        ASSERT(charValue <= 0xffff);
        bufEncrypted[i / 4] = (wchar_t)charValue;
    }
    nValueLen /= 4;
    bufEncrypted[nValueLen] = L'\0';

    // Decrypt this string
    wchar_t* bufDecrypted = new wchar_t[nValueLen+1];
    BlowfishEncryption bf;
    int lenDecrypt = bf.decode((uint8*)bufEncrypted, (uint8*)bufDecrypted, nValueLen * sizeof(wchar_t));
    ASSERT(lenDecrypt == (int)(nValueLen * sizeof(wchar_t)));
    bufDecrypted[nValueLen] = L'\0';

    // Save copy of decrypted string
    String strDecrypted = bufDecrypted;

    // Clean up
    delete [] bufDecrypted;
    bufDecrypted = NULL;
    delete [] bufEncrypted;
    bufEncrypted = NULL;

    // Return decrypted string
    return strDecrypted;
}

uint32 BlowfishEncryption::DecryptUInt32String(const wchar_t* szValue)
{
    // First, decrypt the string.
    String strDecrypt = DecryptString(szValue);

    // Convert decrypted string to uint32.
    wchar_t* pEndBuf;
    uint32 nValue = wcstoul(strDecrypt, &pEndBuf, NUMBER_CONVERT_RADIX);
    return nValue;
}

// The following code was moved over from Cousteau.dll to allow the C++
// BlowfishEncryption class to be used in place of COM.  Eventually it would
// be nice to remove the BSTR logic since it adds no value, but I wanted to
// make the minimum change for now to guarantee compatibility with the COM version.

// Convert an ASCII string of hex characters to its matching binary form
static void hex2bin(char* hex, LPBYTE bin, int hexlen, int datalen)
{
	int i = hexlen - 1, j = datalen - 1;

	for (; i >= 0; i -= 2)
	{
		int curr = 0, prev = 0;

		curr = HexCharToInt(hex[i]);
		if (i >= 1)
			prev = HexCharToInt(hex[i-1]);

		bin[j] = (prev << 4 | curr) & 0xFF;
		j--;
	}
}

static char * _ASCII2Hex(const char *pASCII, int nASCIILength)
{
	char * pHex = new char [nASCIILength * 2 + 1];
	memset (pHex, '\0', nASCIILength * 2 + 1);

	int nIndex = 0;

	while (nIndex < nASCIILength)
	{

		sprintf(&pHex[nIndex * 2], "%02x", (unsigned char)pASCII[nIndex]);

		nIndex++;
	}

	return pHex;
}

static char * _Hex2ASCII(const char *pHex, int nHexLength)
{
	// Alex at Bodog discovered that if we get an invalid hex string
	// with an odd number of bytes we were hitting a buffer overrun
	int nFinalLength = (nHexLength + 1) / 2;

	LPBYTE bytData = new BYTE [nFinalLength + 1];

	memset(bytData, '\0', nFinalLength);
	
	hex2bin ((char *) pHex, bytData, nHexLength, nFinalLength);

	*(bytData + nFinalLength) = '\0';

	return (char *) bytData;
}

static bool EncryptBSTR(BlowfishEncryption& bf, const BSTR bstrPlainText, const BSTR bstrKey, BSTR * bstrCypherText)
{
	if ( bstrPlainText == NULL )
		return false;

	if ( bstrKey == NULL )
		return false;

	int nBufferLength = wcslen(bstrPlainText);
	int nBufferSize = nBufferLength + 1;
	if ( nBufferLength == 0 )
		return false;

	int nKeyLength = wcslen(bstrKey);
	int nKeySize = nKeyLength + 1;
	if ( nKeyLength == 0 )
		return false;

	uint8 * szBuffer	= new uint8[nBufferSize];
	memset(szBuffer, '\0', nBufferSize);
	StringConversions::convert( (char*) szBuffer, (const uint16*) bstrPlainText, nBufferSize );

	uint8* sKey = new uint8[nKeySize];
	memset( sKey, '\0', nKeySize);
	StringConversions::convert( (char*) sKey, (const uint16*) bstrKey, nKeySize );
	int32 nEncryptedLength = bf.encode(	szBuffer, 
										szBuffer, 
										nBufferSize,
										sKey, 
										nKeyLength, 
										(uint8*)BLOWFISH_IVEC, 
										BLOWFISH_IVEC_LEN);


	// convert to HEX to avoid problems in web page usage
	char * pHexEncodedText = _ASCII2Hex((const char *)szBuffer, nEncryptedLength);

	delete[] szBuffer; 
	delete[] sKey;
	
	String str ( pHexEncodedText );

	delete [] pHexEncodedText;

	*bstrCypherText = ::SysAllocString( str );

	return true;
}

static bool DecryptBSTR(BlowfishEncryption& bf, const BSTR bstrCypherText, const BSTR bstrKey, BSTR *bstrPlainText)
{
	if ( bstrCypherText == NULL )
		return false;

	if ( bstrKey == NULL )
		return false;

	int nCypherLength = wcslen( bstrCypherText );

	// [AO 2007-09-27] Odd length indicates not a valid hex string
	ASSERT(nCypherLength % 2 == 0);
	if (nCypherLength == 0 || nCypherLength % 2)
		return false;

	int nKeyLength = wcslen( bstrKey );
	int nKeySize = nKeyLength + 1;
	if ( nKeyLength == 0)
		return false;

	int nCypherSize = nCypherLength + 1;
	uint8 * szCypherText = new uint8[nCypherSize];
	memset(szCypherText, '\0', nCypherSize);
	StringConversions::convert( (char*) szCypherText, (const uint16*) bstrCypherText, nCypherSize );

	uint8* sKey = new uint8[nKeySize];
	memset( sKey, '\0', nKeySize);
	StringConversions::convert( (char*) sKey, (const uint16*) bstrKey, nKeySize );

	// assumes text excrypted by BlowfishEncryption::EncryptBSTR, thus placing it in HEX for originally

	char * pASCIIText = _Hex2ASCII((char *)szCypherText, nCypherLength);

	int nBufferLength	= nCypherLength / 2 + nCypherLength % 2;

	uint8 * szBuffer	= new uint8 [nBufferLength+1];

	memset(szBuffer, '\0', nBufferLength+1);

	memcpy(szBuffer, pASCIIText, nBufferLength);

	bf.decode(	szBuffer, 
				szBuffer, 
				nBufferLength, 
				sKey, 
				nKeyLength, 
				(uint8*)BLOWFISH_IVEC, 
				BLOWFISH_IVEC_LEN);

	String str = (const char*) szBuffer;
	delete[] pASCIIText;
	delete[] szBuffer;
	delete[] sKey;
	delete[] szCypherText;

	*bstrPlainText = ::SysAllocString( str );

	return true;
}

String BlowfishEncryption::EncryptCredential( const String& str )
{
	BSTR bstrEncrypted = NULL;
	String sEncrypted;
	String sKey = BLOWFISH_KEY;
	if ( EncryptBSTR( *this, 
					  (const BSTR) (const uint16*) str, 
					  (const BSTR) (const uint16*) sKey, 
					  &bstrEncrypted ) )
	{
		sEncrypted = bstrEncrypted;
	}

	if ( bstrEncrypted )
	{
		::SysFreeString(bstrEncrypted);
	}
	
	return sEncrypted;
}

String BlowfishEncryption::DecryptCredential( const String& str )
{
	BSTR bstrPlain = NULL;
	String sPlain;
	String sKey = BLOWFISH_KEY;
	if ( DecryptBSTR( *this,
					  (const BSTR) (const uint16*) str, 
					  (const BSTR) (const uint16*) sKey, 
					  &bstrPlain ) )
	{
		sPlain = bstrPlain;
	}
	
	if ( bstrPlain )
	{
		::SysFreeString(bstrPlain);
	}
	return sPlain;
}

#endif